package middleware

import (
	"net/http"
	"strings"
	"time"

	"gitee.com/huangxinbo/xcas/internal/app/base"
	"gitee.com/huangxinbo/xcas/internal/constants"
	"gitee.com/huangxinbo/xcas/internal/model/db_model"
	jwtInstance "gitee.com/huangxinbo/xcas/pkg/jwt"
	"gitee.com/huangxinbo/xcas/pkg/response"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

// 白名单路径（格式：method:path）
var whiteList = []string{
	"get:/api/app/health",
	"get:/api/app/config",
	"post:/api/captcha/generate",
	"post:/api/cas/login",
	"post:/api/cas/register",
	"post:/api/cas/register/verify",
	"post:/api/cas/service-validate",
	"get:/api/cas/st",
	"post:/api/cas/logout",
	"post:/api/cas/forgot",
	"get:/api/cas/forgot/verify",
	"get:/api/oauth/callback",
	"post:/api/oauth/login",
}

var authByCookieTokenList = []string{
	"get:/api/file/image",
}

// AuthMiddleware 认证中间件
func (m *Middleware) AuthMiddleware(base *base.Base) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		// 获取请求方法和路径
		method := ctx.Request.Method
		path := ctx.Request.URL.Path

		// 检查是否在白名单中
		if isInWhiteList(method, path) {
			ctx.Next()
			return
		}

		j := jwtInstance.NewJWT(base.Config)

		if isInAuthByCookieTokenList(method, path) {
			tokenString, err := ctx.Cookie(constants.TOKEN)
			if err != nil {
				response.Error[any](ctx, response.UNAUTHORIZED, m.I18n.T("Invalid token"), nil)
				ctx.Abort()
				return
			}
			// 验证 JWT token
			_, err = j.ValidateToken(ctx, tokenString)
			if err != nil {
				response.Error[any](ctx, response.UNAUTHORIZED, m.I18n.T("Invalid token"), nil)
				ctx.Abort()
				return
			} else {
				ctx.Next()
				return
			}
		}

		// 从 Authorization header 获取 token
		authHeader := ctx.GetHeader("Authorization")
		if authHeader == "" {
			response.Error[any](ctx, response.UNAUTHORIZED, m.I18n.T("Authorization header is required"), nil)
			ctx.Abort()
			return
		}

		// 检查 Bearer token 格式
		parts := strings.Split(authHeader, " ")
		if len(parts) != 2 || parts[0] != "Bearer" {
			response.Error[any](ctx, response.UNAUTHORIZED, m.I18n.T("Invalid authorization header format"), nil)
			ctx.Abort()
			return
		}

		tokenString := parts[1]
		if tokenString == "" {
			response.Error[any](ctx, response.UNAUTHORIZED, m.I18n.T("Token is required"), nil)
			ctx.Abort()
			return
		}

		// 验证 JWT token
		claims, err := j.ValidateToken(ctx, tokenString)
		if err != nil {
			response.Error[any](ctx, response.UNAUTHORIZED, m.I18n.T("Invalid token"), nil)
			ctx.Abort()
			return
		}

		// 从 token 中获取用户 ID
		userID := claims.Subject
		if userID == "" {
			response.Error[any](ctx, response.UNAUTHORIZED, m.I18n.T("Invalid user ID in token"), nil)
			ctx.Abort()
			return
		}

		// 创建用户信息对象（从 JWT 扩展字段获取）
		user := &db_model.User{
			ID:     userID,
			Name:   claims.Name,
			Email:  claims.Email,
			Image:  stringToPtr(claims.Image),
			Role:   claims.Role,
			Region: stringToPtr(claims.Region),
		}

		// 将用户信息存储到上下文中
		ctx.Set("user", user)
		ctx.Set("userId", user.ID)

		// update access_token expired time
		accessToken, tokenExpire, err := j.RefreshToken(tokenString)
		if err != nil {
			base.Logger.Error("Failed to generate JWT token",
				zap.String("user_id", user.ID),
				zap.Error(err))
		}

		// accessToken 写入 cookie
		ctx.SetSameSite(http.SameSiteLaxMode)
		ctx.SetCookie(constants.TOKEN, accessToken, int(time.Until(tokenExpire).Seconds()), "/", "", false, false)

		ctx.Next()
	}
}

// stringToPtr 将字符串转换为字符串指针
func stringToPtr(s string) *string {
	if s == "" {
		return nil
	}
	return &s
}

// isInAuthByCookieTokenList 检查路径是否需要通过 cookie 的 access_token 进行验证
func isInAuthByCookieTokenList(method, path string) bool {
	// 构建当前请求的完整路径标识
	currentPath := strings.ToLower(method) + ":" + path

	// 检查是否需要通过 cookie 的 access_token 进行验证
	for _, whitePath := range authByCookieTokenList {
		if strings.HasPrefix(currentPath, whitePath) {
			return true
		}
	}

	// 除了 /api/user 接口外，其他接口都需要认证
	// 所以 /api/user 不在白名单中，需要认证
	return false
}

// isInWhiteList 检查路径是否在白名单中
func isInWhiteList(method, path string) bool {
	// 构建当前请求的完整路径标识
	currentPath := strings.ToLower(method) + ":" + path

	// 检查是否在白名单中
	for _, whitePath := range whiteList {
		if strings.HasPrefix(currentPath, whitePath) {
			return true
		}
	}

	// 除了 /api/user 接口外，其他接口都需要认证
	// 所以 /api/user 不在白名单中，需要认证
	return false
}

// GetUserFromContext 从上下文中获取用户信息
func GetUserFromContext(ctx *gin.Context) (*db_model.User, bool) {
	user, exists := ctx.Get("user")
	if !exists {
		return nil, false
	}

	if userObj, ok := user.(*db_model.User); ok {
		return userObj, true
	}

	return nil, false
}

// GetUserIDFromContext 从上下文中获取用户ID
func GetUserIDFromContext(ctx *gin.Context) (string, bool) {
	userID, exists := ctx.Get("userID")
	if !exists {
		return "", false
	}

	if userIDStr, ok := userID.(string); ok {
		return userIDStr, true
	}

	return "", false
}
